/* Copyright (c) 2006 YourNameHere
   See the file LICENSE.txt for licensing information. */
var pow_bc_root_directory = pow_bc_get_root_directory();

// PUBLIC API //
/*
 *
 * Reads a file, binary by default.
 *
 */

function pow_file(readfile) {
	try {
		var read_flags = "rb";
		var offset = 0;
		var length = 0;
		var absolute_path = "";
		var file_contents = "";
		if (readfile.match(/^\//)) {
			absolute_path = readfile;
		} else if (readfile.match(/^http:\/\//)) {
			file_contents = pow_download_file(readfile);
			return file_contents;
		} else {
			absolute_path = pow_bc_root_directory + pow_request.req_dir + "/" + readfile;
		}
		file_contents = pow_get_file_at_offset(absolute_path, offset, length, read_flags);
		// Content-Length is normally used for images
		file_contents = file_contents.replace(/(.|\n|\r)*Content-Length: \d+\r\n\r\n/m, "");
		file_contents = file_contents.replace(/Content-Location: .*\r\n\r\n/m, "");
		return file_contents;
	} catch(e) {
		log_error(e);
		return "";
	}
}

/*
 *
 * Returns a single line, depending on line separator
 *
 */

function pow_file_line(fstream) {
	try {
		var line = {}
		var hasmore = fstream.readLine(line);
		//if(line == undefined) { return null; }
		if (!line.value && !hasmore) {
			return null;
		}
		var simple_line = line.value;
		return simple_line + "\n";
	} catch(e) {
		log_error(e);
	}
	return "";
}

/*
 *
 * Closes a line input stream
 *
 */

function pow_file_close(fstream) {
	try {
		fstream.close();
	} catch(e) {
		log_error(e);
	}
}

/*
 *
 * Opens a line input stream
 *
 */

function pow_file_open(filename, rwa_flags) {
	try {
		var file = pow_file_get_handle(filename);
		if (!file.exists()) {
			return "";
		}
		var write_flags = 0x01;
		if (rwa_flags == "w") {
			write_flags = 0x02 | 0x08 | 0x20;
			fstream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
			fstream.init(file, write_flags, 0644, 0);
		} else {
			// "r"
			write_flags = 0x01;
			fstream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
			fstream.init(file, write_flags, 0, 0);
			fstream.QueryInterface(Components.interfaces.nsILineInputStream);
		}
		return fstream;
	} catch(e) {
		log_error(e);
	}
	return "";
}

/*
 *
 * Writes contents to a file using flags.
 * w - write ascii
 * wb - write binary
 *
 */

function pow_file_put_contents(filename, contents, rwa_flags) {
	try {
		var offsets = new Array();
		var data = new Array();
		data.push(contents);
		offsets.push(0);
		var absolute_path = "";
		if (filename.match(/^\//)) {
			absolute_path = filename;
		} else {
			absolute_path = pow_bc_root_directory + pow_request.req_dir + "/" + filename;
		}
		pow_save_file_at_offset(absolute_path, data, offsets, rwa_flags);
	} catch(e) {
		log_error(e);
	}
	return;
}

/*
 *
 * Creates a directory relative to the sjs script.
 *
 */

function pow_file_mkdir(dirname) {
	pow_mkdir(dirname);
}

/*
 *
 * Deletes a file.
 *
 */

function pow_file_delete(filename) {
	try {
		pow_delete_file(filename, false);
	} catch(e) {
		log_error(e);
	}
	return;
}

/*
 *
 * Downloads a file from the web.
 *
 */

function pow_download_file(url) {
	var page = pow_remote_request(url, "GET", "");
	return page;
}

/*
 *
 * Returns boolean existence of a file.
 *
 */

function pow_file_exists(filename) {
	try {
		var fh = fileGetHandle(filename);
		if (fh.exists()) {
			return true;
		} else {
			return false;
		}
	} catch(e) {
		log_error(e);
		return false;
	}
}

/*
 *
 * Executes a command line script (PUBLIC)
 *
 */

function pow_exec(path, args, blocking) {
	try {
		var res;
		var script_handle = pow_file_get_handle(path);
		var root_dir = pow_file_get_handle("/");
		var pipe_args = new Array(script_handle.path, script_handle.parent.path, root_dir.path);
		pipe_args = pipe_args.concat(args);
		if (navigator.oscpu.match(/Windows/)) {
			res = pow_pipe_exec("/pow/bin/makepipe.bat", pipe_args, blocking);
		} else {
			res = pow_pipe_exec("/pow/bin/makepipe.sh", pipe_args, blocking);
		}
		return res;
	} catch(e) {
		log_error(e);
		return false;
	}
}

/*
 *
 * Executes a command line script. (PRIVATE)
 * Root is POW root
 *
 */

function pow_pipe_exec(filename, args, blocks) {
	try {
		var f = pow_file_get_handle(filename);
		if (f.exists() == false) {
			return "";
		}
		var runner = Components.classes["@mozilla.org/process/util;1"].createInstance(Components.interfaces.nsIProcess);
		runner.init(f);
		runner.run(blocks, args, args.length);
		pow_server.EXIT_VALUE = runner.exitValue;
		var text = pow_file("/pow/data/powpipe.txt");
		return text;
	} catch(e) {
		log_error(e);
		return false;
	}
}

/*
 *
 * Returns /pow/data
 *
 */

function pow_file_get_data_dir() {
	try {
		return pow_bc_get_data_directory();
	} catch(e) {
		log_error(e);
		return false;
	}
}

/*
 *
 * Returns /pow/log
 *
 */

function pow_file_get_log_dir() {
	try {
		return pow_bc_get_log_directory();
	} catch(e) {
		log_error(e);
		return false;
	}
}

/*
 *
 * Returns /pow/htdocs
 *
 */

function pow_file_get_docs_dir() {
	try {
		return pow_bc_get_root_directory();
	} catch(e) {
		log_error(e);
		return false;
	}
}

/*
 *
 * Database object.
 *
 */

function pow_DB(db_name) {

	/*
  *
  * Creates database if it does not exist.
  * Holds connection open.
  *
  */

	this.open = function(db_name) {
		try {
			var new_file = pow_file_get_handle("/");
			var data_dir = pow_bc_get_data_directory();
			new_file = pow_append_path(new_file, data_dir + "/");
			new_file.append(db_name + ".sqlite");
			var storageService = Components.classes["@mozilla.org/storage/service;1"].getService(Components.interfaces.mozIStorageService);
			var mDBConn = storageService.openDatabase(new_file);
			return mDBConn;
		} catch(e) {
			log_error(e);
			return false;
		}
	};

	/*
  *
  * Deletes database file.
  *
  */

	this.drop_database = function(db_name) {
		try {
			this.conn = null; // Added for XP
			var data_dir = pow_bc_get_data_directory();
			pow_delete_file(data_dir + "/" + db_name + ".sqlite", false);
		} catch(e) {
			log_error(e);

		}

	};

	/*
  *
  * Connection object. Should be closed later.
  *
  */

	this.conn = this.open(db_name);

	/*
  *
  * Prints a table from the results object.
  *
  */

	this.pretty_print = function(results) {
		try {
			var table;
			table = "<pre>";
			var separator = "";
			var this_row = new Array();
			for (var j = 0; j < results["column_names"].length; j++) {
				for (var k = 0; k < results["column_widths"][j]; k++) {
					if (j || k) {
						separator += "-";
					} else {
						separator += " ";
					}
				}
				separator += "---";
			}
			separator += "\n";

			table += "\n";
			table += separator;
			for (var i = -1; i < results.length; i++) {
				table += "| ";
				if (i == -1) {
					this_row = results["column_names"];
				} else {
					this_row = results[i];
				}
				var column_row_length = 0;
				for (var j = 0; j < this_row.length; j++) {
					if (this_row[j] == null) {
						column_row_length = 4;
					} else {
						column_row_length = this_row[j].length;
					}
					for (var k = 0; k < results["column_widths"][j] - column_row_length; k++) {
						table += " ";
					}
					table += this_row[j];
					table += " | ";
				}
				table += "\n";
				table += separator;
			}
			table += "</pre>";
			pow_response.script_out += table;
		} catch(e) {
			log_error(e);
		}

	};

	/*
  *
  * Executes database SQL call.
  *
  */

	this.exec = function(statement) {
		try {
			var row = new Array();
			var column_names = new Array();
			var column_widths = new Array();
			var prep_statement = this.conn.createStatement(statement);
			if (!prep_statement) {
				return "";
			}
			prep_statement.execute();
			var count = 0;
			if (statement.match(/SELECT/i)) {
				while (prep_statement.executeStep()) {
					row[count] = new Array();
					if (!count) {
						for (var i = 0; i < prep_statement.columnCount; i++) {
							var name = prep_statement.getColumnName(i);
							column_widths[i] = name.length;
							column_names[i] = name;
						}
					}
					for (var i = 0; i < prep_statement.columnCount; i++) {
						var data = prep_statement.getUTF8String(i);
						if (data && data.length > column_widths[i]) {
							column_widths[i] = data.length;
						}
						row[count].push(data);
					}
					count++;
				}
			}
			row["column_names"] = column_names;
			row["column_widths"] = column_widths;
			return row;
		} catch(e) {
			log_error(e);
			return false;
		}
	};
	// I DO not know what this does
	this.reset = function(statement) {
		statement.reset();
	}
}

/*
 * Returns nsIFile object array
 */

function pow_file_list_dir(dirname) {
	var dir_list = new Array();
	var f = pow_file_get_handle(dirname);
	if (f.exists() && f.isDirectory()) {
		var dir_stack = new Array(f);
		while (dir_stack.length) {
			var entries = dir_stack.pop().directoryEntries;
			while (entries.hasMoreElements()) {
				var entry = entries.getNext();
				entry.QueryInterface(Components.interfaces.nsIFile);
				dir_list.push(entry);
			}
		}
	} else if (f.exists()) { // file 
		dir_list.push(f);
	}
	return dir_list;
}

/*
 *
 * pow_remote_post()
 * POST sends key value pairs to server
 *
 */

function pow_remote_post(url, params) {
	var method = "POST";
	var page = pow_remote_request(url, method, params);
	return page;
}

/*
 *
 * pow_remote_put()
 * sends PUT and key value pairs to server
 *
 */

function pow_remote_put(url, params) {
	var method = "PUT";
	var page = pow_remote_request(url, method, params);
	return page;
}

/*
 *
 * pow_remote_head()
 * sends HEAD request to server
 *
 */

function pow_remote_head(url, params) {
	var method = "HEAD";
	var page = pow_remote_request(url, method, params);
	return page;
}

/*
 *
 * pow_remote_delete()
 * sends DELETE request to server
 *
 */

function pow_remote_delete(url, params) {
	var method = "DELETE";
	var page = pow_remote_request(url, method, params);
	return page;
}

// END PUBLIC API //
function pow_remote_request(url, method, params) {
	var page = "";
	var req = new XMLHttpRequest();
	req.overrideMimeType('text/plain; charset=x-user-defined'); // binary
	if (req) {
		req.open(method, url, false);
		if (method == "POST") {
			req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
			req.setRequestHeader("Content-length", params.length);
			req.setRequestHeader("Connection", "close");
		}
		req.send(params);
		if (req.status == 200) {
			if (method == "HEAD") {
				return req.getAllResponseHeaders();
			}
			page = req.responseText;
			req.getResponseHeader("Content-Type")
		} else {
			return "";
		}
	}
	return page;
}

/**
 *
 * Returns an array of all files matching regex
 *
 */

function pow_file_find_matching(dirname, regex) {
	var file_list = new Array();
	var find_regex = new RegExp(regex);
	var dir_stack = new Array(pow_get_nsifile(dirname));
	var testit = pow_get_nsifile("/breadcrumbs");
	while (dir_stack.length) {
		var entries = dir_stack.pop().directoryEntries;
		while (entries.hasMoreElements()) {
			var entry = entries.getNext();
			entry.QueryInterface(Components.interfaces.nsIFile);
			if (entry.isDirectory()) {
				dir_stack.push(entry);
			} else {
				if (entry.leafName.match(find_regex)) {
					var end_file = entry.path.replace(/.*(\/breadcrumbs\/pages)/, "/breadcrumbs/pages");
					file_list.push(end_file);
				}
			}
		}
	}
	return file_list;
}

/**
 *
 * POSTs a file, filename to location url
 *
 */

function pow_upload_file(url, filename) {
	try {
		var port = 80;
		var timeout = 5;
		var leafname = "";
		var leafname_m = filename.match(/([^\/]*)$/);
		if (!leafname_m) {
			return "";
		}
		leafname = leafname_m[1];
		var file_contents = pow_file(filename);
		if (!file_contents) {
			return "";
		}
		url_parts = url.match(/http:\/\/([^:\/]+)(:\d+)?(.*)/);
		if (url_parts) {
			if (url_parts[2]) {
				port = url_parts[2];
			}
			var delimiter = "---------------------------168072824752491622650073";
			var whole_file = "--" + delimiter + "\r\n" + "Content-Disposition: form-data; name=\"MAX_FILE_SIZE\"\r\n" + "\r\n" + "100000\r\n" + "--" + delimiter + "\r\n" + "Content-Disposition: form-data; name=\"userfile\"; filename=\"" + leafname + "\"\r\n" + "Content-Type: text/plain\r\n" + "\r\n" + file_contents + "\r\n" + "--" + delimiter;

			var query = "POST " + url_parts[3] + " HTTP/1.1\r\n" + "Host: localhost:6670\r\n" + "User-Agent: POW/0.0.5\r\n" + "Accept: text/xml,application/xml,application/xhtml+xml," + "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\n" + "Accept-Language: en-us,en;q=0.5\r\n" + "Accept-Encoding: gzip,deflate\r\n" + "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" + "Keep-Alive: 300\r\n" + "Connection: keep-alive\r\n" + "Referer: http://localhost/~davidkellogg/test/upload.php\r\n" + "Content-Type: multipart/form-data; boundary=" + delimiter + "\r\n" + "Content-Length: " + whole_file.length + "\r\n" + "\r\n" + whole_file + "--";
			var page = pow_read_from_socket(url_parts[1], port, query, timeout);
			return page;
		} else {
			return "";
		}
	} catch(e) {
		log_error(e);
		return false;
	}
}

/**
 *
 * Returns handle for root where /pow and /extensions are located
 * Or gives user-defined location.
 *
 */

function pow_file_get_root_handle() {
	try {
		var file = pow_file_get_handle("/");
		file = pow_append_path(file, pow_bc_get_root_directory());
		return file.path;
	} catch(e) {
		log_error(e);
		return false;
	}
}

/**
 *
 * Returns string from socket read from host:port given a timeout
 * outputData is a raw 'GET ... \r\n\r\n' or POST with data
 *
 */

function pow_read_from_socket(host, port, outputData, timeout) {
	try {
		var output = "";
		var transportService = Components.classes["@mozilla.org/network/socket-transport-service;1"].getService(Components.interfaces.nsISocketTransportService);
		var transport = transportService.createTransport(null, 0, host, port, null);

		var outstream = transport.openOutputStream(1, 0, 0);
		// TRY Seems to work well
		var bos = Components.classes["@mozilla.org/binaryoutputstream;1"].createInstance(Components.interfaces.nsIBinaryOutputStream);
		bos.setOutputStream(outstream);
		// END TRY
		bos.write(outputData, outputData.length);

		transport.setTimeout(1, timeout);
		var stream = transport.openInputStream(1, 100, 100000);

		var bis = Components.classes["@mozilla.org/binaryinputstream;1"].createInstance(Components.interfaces.nsIBinaryInputStream);
		bis.setInputStream(stream);

		var instream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
		instream.init(stream);
		var output = "";
		var size = 0;
		var i;
		// Blocking first byte
		output = bis.readBytes(1);
		for (i = 0; i < 1000000; i++) {
			if (instream) {
				try {
					size = instream.available();
				} catch(e) {
					instream.close();
					outstream.close();
					bis.close();
					return null;
				}
			}
			if (size) {
				output += bis.readBytes(size);
				break;
			}
		}
		try {
			var trash = "";
			for (var i = 0; i < 10000000; i++) {
				output += bis.readBytes(1);
			}
		} catch(e) {
			// Not enough data
		}
		bis.close();
		instream.close();
		outstream.close();
		return output;
	} catch(e) {
		log_error(e);
		return false;
	}

}

// CLOBBER IS NOT IMPLEMENTED
/**
 *
 * Copies a file from source to dest
 * If clobber is false, the file cannot be overwritten
 *
 */

function pow_copy_file(source, dest, clobber) {
	try {
		if (!source || !dest) {
			return ""; // empty string
		}
		var m = source.match(/([^\/]+)$/);
		var source_file = m[1];
		var from; // FROM is an immutable dir location
		var dirservice = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties);
		if (source.match(/^__DEFAULT__/)) { // FF mother dir
			source = source.replace(/^__DEFAULT__/, "");
			from = dirservice.get('ProfD', Components.interfaces.nsILocalFile);
		} else if (source.match(/^__DEFAULT_XR__/)) { // XULRunner dir
			source = source.replace(/^__DEFAULT_XR__/, "");
			from = dirservice.get('resource:app', Components.interfaces.nsILocalFile);
		} else {
			from = pow_file_get_handle("/"); // From default or user-specified root dir
		}
		from = pow_append_path(from, source);
		if (from.exists() == false) {
			return "";
		}
		// serv_dir_file = dest;
		var to = pow_file_get_handle("/"); // root
		var to_check = pow_file_get_handle("/");
		to = pow_append_path(to, dest);
		to_check = pow_append_path(to_check, dest + "/" + source_file);
		if (to_check.exists() == true && clobber == false) { // to file exists
			return ""; // cannot clobber this file
		}
		if (to_check.exists()) {
			to_check.remove(false);
		}
		if (!to_check.exists()) {
			from.copyTo(to, "");
		}
	} catch(e) {
		log_error(e);
		return "";
	}
	return "";

}

/**
 *
 * Saves contents in filename using rwa_flags
 * Acceptable flags include wb (write binary), wba (write binary append)
 *
 */

function pow_save_file(filename, contents, rwa_flags) {
	if (!filename) {
		return "";
	}
	var offsets = new Array();
	var data = new Array();
	data.push(contents);
	offsets.push(0);
	pow_save_file_at_offset(filename, data, offsets, rwa_flags);
	return true;
}

/**
 *
 * Returns file size
 * Useful for binary data
 *
 */

function pow_file_get_size(filename) {
	try {
		var fh = pow_file_get_handle(filename);
		if (!fh.exists()) {
			return 0;
		}
		return fh.fileSize;
	} catch(e) {
		log_error(e);
		return "";
	}
}

function fileGetHandle(filename) {
	try {
        var dirservice = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties);
        var f = dirservice.get('ProfD', Components.interfaces.nsILocalFile);
        return f.append(filename);
	}
        catch(e) {
		log_error(e);
		return "";
	}
}

function pow_save_file_at_offset(filename, data, offsets, rwa_flags) {
	if (!filename) {
		return "";
	}
	if (!rwa_flags) {
		rwa_flags = "wb";
	}
	var f = pow_file_get_handle(filename);

	if (f.exists() == false) {
		try {
			f.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0644);
		} catch(e) {
			log_error("failed writing");
		}
	} else if (rwa_flags && offsets[0] == 0 && offsets.length == 1 && rwa_flags.match(/^wb?$/)) {
		f.remove(false);
	}
	// SAVE file
	var write_flags = 0x02 | 0x08
	// overwrite
	if (rwa_flags == "w") {
		write_flags = 0x02 | 0x08 | 0x20;
		// append
	} else if (rwa_flags == "wa") {
		write_flags = 0x02 | 0x10;
		// write binary
	} else if (rwa_flags == "wb") {
		write_flags = 0x02 | 0x08 | 0x20;
		// write binary append
	} else if (rwa_flags == "wba") {
		write_flags = 0x02 | 0x10;
		// read and write
	} else if (rwa_flags == "ws") {
		write_flags = 0x04 | 0x08;
		// read and write
	} else if (rwa_flags == "ws+") {
		write_flags = 0x04 | 0x08;
	}
	try {
		if ((rwa_flags == "ws" || rwa_flags == "ws+") && offsets[0] > 0) {

			if (rwa_flags == "ws+") {
				var sis = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
				var is = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
				is.init(f, write_flags, 0644, 0);
				is.QueryInterface(Components.interfaces.nsISeekableStream);
				if (offsets[0] > 0) {
					is.seek(0, offsets[0]);
				}
				sis.init(is);
				var appendix = "";
				if (sis.available() < 10000000) {
					appendix = sis.read(sis.available());
				}
				data[0] += appendix;
				is.close();
				sis.close();
			}
		}
		var outputStream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
		outputStream.init(f, write_flags, 0644, 0);

		for (var i = 0; i < offsets.length; i++) {
			if (offsets[i] > 0) {
				outputStream.QueryInterface(Components.interfaces.nsISeekableStream);
				outputStream.seek(0, offsets[i]);
			}
			var result = outputStream.write(data[i], data[i].length);
		}
		outputStream.close();
	} catch(e) {
		log_error(e);
		return "";
	}
	return "";
}

/*
    make dir 
*/
function mkdir(dirname) {
	if (!dirname) {
		return "";
	}
	var f = fileGetHandle(dirname);
	if (f.exists() == false) {
		try {
			f.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
		} catch(e) {
			log_error(e);
			return "";
		}
	}
	return "";
}

function pow_delete_file(filename, recursive) {
	try {
		if (!recursive) {
			recursive = false;
		}
		// User profile directory
		var f = pow_file_get_handle(filename);
		if (f.exists() == false) {
			return "";
		}
		f.remove(recursive);
	} catch(e) {
		log_error(e);
		return "";
	}
	return "";
}

function pow_get_file(readfile, read_flags) {
	try {
		var offset = 0;
		var length = 0;
		var file_contents = pow_get_file_at_offset(readfile, offset, length, read_flags);
		file_contents = file_contents.replace(/Content-Length: \d+\r\n\r\n/m, "");
		return file_contents;
	} catch(e) {
		log_error(e);
		return "";
	}
}

function pow_get_nsifile(readfile) {
	// A LITTLE SECURITY
	readfile = readfile.replace(/\.{2,}/g, "");
	var display_path = readfile;
	var internal_path = readfile;

	display_path = display_path.replace(/^\/breadcrumbs\/htdocs\//, "");
	display_path = display_path.replace(/^\/pow\/htdocs\//, "");
	internal_path = internal_path.replace(/htdocs\/breadcrumbs\//, "");
	internal_path = internal_path.replace(/htdocs\/pages\//, "pages/");
	display_path = display_path.replace(/^\//, "");
	content_location = "/" + display_path;

	try {
		dir_path = internal_path;
		var f_base = pow_file_get_handle("/");
		var f;

		if (internal_path.match(/\/$/)) {
			// A DIRECTORY
			file_path = dir_path + "index.sjs";
			f = pow_append_path(f_base, file_path);
			if (f.exists()) {
				content_location += "index.sjs";
			} else {
				file_path = dir_path + "index.html";
				f_base = pow_file_get_handle("/");
				f = pow_append_path(f_base, file_path);
				if (f.exists()) {
					content_location += "index.html";
				} else {
					file_path = dir_path;
					f_base = pow_file_get_handle("/");
					f = pow_append_path(f_base, file_path);
				}
			}
		} else {
			f_base = pow_file_get_handle("/");
			f = pow_append_path(f_base, internal_path);
		}
	} catch(e) {
		log_error(e);
		return "";
	}
	return f;
}

function pow_get_file_at_offset(readfile, offset, length, read_flags) {
	// A LITTLE SECURITY
	readfile = readfile.replace(/\.{2,}/g, "");
	var dir_path = "";
	var file_path = "";
	var content_location = "";
	if (!read_flags) {
		return "ERROR: File not found";
	}

	var display_path = readfile;
	var internal_path = readfile;

	display_path = display_path.replace(/^\/breadcrumbs\/htdocs\//, "");
	display_path = display_path.replace(/^\/pow\/htdocs\//, "");
	internal_path = internal_path.replace(/htdocs\/breadcrumbs\//, "");
	internal_path = internal_path.replace(/htdocs\/pages\//, "pages/");
	display_path = display_path.replace(/^\//, "");
	content_location = "/" + display_path;
	dir_path = internal_path;

	// FEED default page for dir
	try {
		dir_path = internal_path;
		var f_base = pow_file_get_handle("/");
		var f;
		if (internal_path.match(/\/$/)) {
			// A DIRECTORY
			file_path = dir_path + "index.sjs";
			f = pow_append_path(f_base, file_path);
			if (f.exists()) {
				content_location += "index.sjs";
			} else {
				file_path = dir_path + "index.html";
				f_base = pow_file_get_handle("/");
				f = pow_append_path(f_base, file_path);
				if (f.exists()) {
					content_location += "index.html";
				} else {
					file_path = dir_path;
					f_base = pow_file_get_handle("/");
					f = pow_append_path(f_base, file_path);
				}
			}
		} else {
			f_base = pow_file_get_handle("/");
			f = pow_append_path(f_base, internal_path);
		}
	} catch(e) {
		log_error(e);
		return "";
	}

	if (dir_path) {
		try {
			if (f.exists() == false) {
				return "ERROR: File or Directory Not Found";
			}
			var file_list_out = "";
			var prepend_breadcrumbs = "<a href='/" + display_path;
			var append_breadcrumbs = "'>";
			var file_list_out = "";
			if (pow_bc_root_directory.match(/breadcrumbs/)) {
				file_list_out += "<br><br><center>" + pow_get_search_form('') + "</center>";
			}
			file_list_out += "<br><br><br><br>\n";
			//  DIR
			if (f.isDirectory()) {
				if (content_location.match(/[^\/]$/)) {
					content_location += "/";
				}
				var entries = f.directoryEntries;
				var file_array = [];
				while (entries.hasMoreElements()) {
					var entry = entries.getNext();
					entry.QueryInterface(Components.interfaces.nsIFile);
					file_array.push(entry);
					if (entry.isDirectory()) {
						file_list_out += prepend_breadcrumbs + entry.leafName + "/" + append_breadcrumbs + entry.leafName + "/</a><br>\n";
					} else if (entry.isFile()) {
						file_list_out += prepend_breadcrumbs + entry.leafName + append_breadcrumbs + entry.leafName + "</a><br>\n";
					}
				}
				return "Content-Location: " + content_location + "\r\nContent-Length: " + file_list_out.length + "\r\n\r\n" + file_list_out;
			}
		} catch(e) {
			log_error(e);
			return "";
		}
	} else {
		if (f.exists() == false) {
			return "ERROR: File Not Found";
		}
	}

	try {
		// OPEN stream
		var is = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);

		var output;
		if (read_flags != "rb") {
			// ASCII file
			var sis = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
			if (offset > 0) {
				is.QueryInterface(Components.interfaces.nsISeekableStream);
			}
			is.init(f, 0x01, 00004, 0);
			sis.init(is);
			if (offset > 0 || length > 0) {
				if (offset > 0) {
					is.seek(0, offset);
				}
				output = "\r\nContent-Length: " + f.fileSize + "\r\n\r\n";
				if (length > sis.available()) {
					output += sis.read(sis.available());
				} else {
					output += sis.read(length);
				}
			} else {
				output = "\r\nContent-Length: " + f.fileSize + "\r\n\r\n" + sis.read(sis.available());
			}
			is.close();
			sis.close();
		} else {
			// BINARY file
			var data = {};
			is.init(f, 1, 0, false);
			var bis = Components.classes["@mozilla.org/binaryinputstream;1"].createInstance(Components.interfaces.nsIBinaryInputStream);
			bis.setInputStream(is);
			// GET Bytes
			output = "\r\nContent-Length: " + f.fileSize + "\r\n\r\n" + bis.readBytes(f.fileSize);
			bis.close();
			is.close();
		}
	} catch(e) {
		log_error(e);
		return "";
	}
	output = "Content-Location: " + content_location + "\r\n" + output;
	return output;
}

function pow_append_path(file, path) {
	try {
		var dirs = new Array();
		//	var num = 0;
		while (path.match(/^[^\/]*\/.* ?/)) {
			var next_dir = path.replace(/^([^\/]*)\/(.*)/, "$1");
			file.append(next_dir);
			path = path.replace(/^([^\/]*)\/(.*)/, "$2");
		}
		if (path) {
			file.append(path);
		}
		return file;
	} catch(e) {
		log_error(e);
		return "";
	}
}

/*
 *
 * Logs messages and rotates logs
 *
 */

function pow_log(msg, filename, rot_filename) {
	//	try {
	var size = pow_file_get_size(filename);
	if (size > 1000) {
		var old_log = pow_file(filename, msg + "\n");
		pow_file_put_contents(rot_filename, old_log);
		pow_save_file(filename, msg + "\n", "w");
	} else {
		pow_save_file(filename, msg + "\n", "wa");
	}
	/*
	} catch(e) {
	}
*/
}

/*
 *
 * Logs errors and rotates logs
 *
 */

function log_error(e) {
	//	try {
	var root = pow_bc_get_index_directory();

	var date_string = pow_get_pretty_date();
	var error_msg = date_string + e.fileName + " " + e.lineNumber + ": " + e.message;
	pow_log(error_msg, root + "/log/error.txt", root + "/log/error.1.txt");
	/*
	} catch(e) {
	}
*/
}

/*
   *
   * Returns formatted date
   *
	 */

function pow_get_pretty_date() {
	var date = new Date();
	var year = date.getYear() + 1900;
	var month = date.getMonth() + 1;
	var day = date.getDate();
	var hours = date.getHours();
	var minutes = date.getMinutes();
	var seconds = date.getSeconds();

	var date_string = year + "-0" + month + "-0" + day + " 0" + hours + ":0" + minutes + ":0" + seconds;
	date_string = date_string.replace(/(\d+)-\d*(\d{2})-\d*(\d{2}) \d*(\d{2}):\d*(\d{2}):\d*(\d{2})/, "$1-$2-$3 $4:$5:$6 ");
	return date_string;
}

/*
 *
 * pow_session_end() 
 * Called by system if session was initiated
 *
 */

function pow_session_end() {
	var my_db = pow_session_init_db();
	var data = "";
	for (var i in pow_server.SESSION) {
		if (data) {
			data += ";";
		}
		data += i + "=" + pow_server.SESSION[i];
	}
	var expires_str = "";
	var random_cookie = pow_server.SESSION_ID;
	pow_session_add_data(my_db, random_cookie, expires_str, data);
}

/*
 *
 * pow_session_init() 
 * Called by user to initialize session
 *
 */

function pow_session_init(expires_delta) {
	if (!expires_delta) {
		expires_delta = 3600 * 24;
	}
	var my_db = pow_session_init_db();
	// INIT SESSION var
	if (!pow_server.SESSION) {
		pow_server.SESSION = new Array();
	}
	// END var
	// CREATE COOKIE
	var cook = pow_server.REQUEST_HEADERS['Cookie']
	if (!cook) {
		var now = new Date();
		var expires = new Date();
		//expires.setTime(now.getTime()+1000000*3600*24);
		expires.setTime(now.getTime() + 1000 * expires_delta);
		var expires_str = expires.toGMTString();
		var random_cookie = Math.floor(Math.random() * 1000000000).toString(16);
		var cookie = "Set-Cookie: pow_session=" + random_cookie + "; expires=" + expires_str + "; path=/;";
		if (!pow_server.user_id) {
			pow_server.user_id = new Array();
		}
		pow_session_add_data(my_db, random_cookie, expires.getTime(), "");
		pow_header(cookie);
	} else {
		var m1 = cook.match(/pow_session=(.*)/);
		random_cookie = m1[1];
	}
	pow_server.SESSION_ID = random_cookie;
	// END COOKIE
	pow_session_run_gc(my_db);

	// SET SESSION FROM data in DB
	var sess_data = pow_session_get_data(my_db, random_cookie);
	if (sess_data) {
		var data_splits = sess_data.split(";");
		for (var i = 0 in data_splits) {
			var val_splits = data_splits[i].split(/=/);
			if (val_splits[0]) {
				var key = val_splits[0].replace(/_EQUALS_/g, "=");
				key = key.replace(/_SEMICOLON_/g, ";");
				var val = val_splits[1].replace(/_EQUALS_/g, "=");
				val = val.replace(/_SEMICOLON_/g, ";");
				pow_server.SESSION[key] = val;
			}
		}
	}
}

/*
 *
 * pow_session_add_data() 
 * Add session data for cookie-ed user
 *
 */

function pow_session_add_data(pdb, user_id, date, data) {
	var hasdata = pow_session_get_data(pdb, user_id);
	if (!hasdata) {
		if (data) {
			pdb.exec("INSERT INTO sessions (user_id, expires, data) VALUES ('" + user_id + "','" + date + "','" + data + "')");
		}
	} else {
		if (date) {
			pdb.exec("UPDATE sessions set data='" + data + "', expires='" + date + "' where user_id='" + user_id + "';");
		} else {
			pdb.exec("UPDATE sessions set data='" + data + "' where user_id='" + user_id + "';");

		}
	}
}

/*
 *
 * pow_session_init_db() 
 * Initializes session DB
 *
 */

function pow_session_init_db() {
	try {
		var pdb = new pow_DB("pow_sessions");
		pdb.exec("CREATE TABLE sessions (user_id varchar(50), expires int, data blob)");
	} catch(e) {
		// noop
	}
	return pdb;
}

/*
 *
 * pow_session_get_data() 
 * Return data set for cookie
 *
 */

function pow_session_get_data(pdb, user_id) {
	var results = pdb.exec("SELECT * FROM sessions WHERE user_id='" + user_id + "'");
	if (results[0]) {
		return results[0][2];
	}
	return "";
}

/*
 *
 * pow_session_run_gc() 
 * Garbage collector, always a chance to start
 *
 */

function pow_session_run_gc(pdb) {
	// ONE in TWENTY chance of collecting garbage
	var rand = Math.floor(Math.random() * 20);
	if (rand == 1) {
		var now = new Date();
		var results = pdb.exec("DELETE FROM sessions WHERE expires < " + now.getTime());
	}
}
